//-< STOCKDB.CPP >---------------------------------------------------*--------*
// GigaBASE                  Version 1.0         (c) 1999  GARRET    *     ?  *
// (Post Relational Database Management System)                      *   /\|  *
//                                                                   *  /  \  *
//                          Created:     22-Jan-2012   K.A. Knizhnik * / [] \ *
//                          Last update: 22-Jan-2012   K.A. Knizhnik * GARRET *
//-------------------------------------------------------------------*--------*
// Stock database
//-------------------------------------------------------------------*--------*

#ifndef __STOCKDB_H__
#define __STOCKDB_H__

#include "gigabase.h" 
#include "timeseries.h"
#include <stdio.h>
#include <time.h>
#include <string>
#include <vector>
#include <map>

USE_GIGABASE_NAMESPACE
using namespace std;

typedef unsigned int date_t;

const int TIME_BUF_SIZE = 32;

enum ErrorCode 
{ 
    OK,
    FILE_NOT_FOUND,
    FILE_FORMAT_ERROR,
    FILE_WRITE_ERROR,
    FILE_IO_ERROR,
    DATABASE_ERROR,
    NETWORK_ERROR,
    NO_PROVIDER,
    NO_PROCESSOR,
    PROVIDER_ERROR,
    BAD_RESPONSE,
    SYMBOL_NOT_FOUND
};

struct Stock
{
    string name;
    date_t from;
    date_t till;
    int    nQuotes;

    TYPE_DESCRIPTOR((KEY(name, INDEXED), FIELD(from), FIELD(till), FIELD(nQuotes)));
};

struct Quote 
{
    date_t date;
    float  low;
    float  high;
    float  open;
    float  close;
    float  adjClose;
    int    volume;

    time_t time() const { return date; } // this method should be defined 

    TYPE_DESCRIPTOR((FIELD(date), FIELD(low), FIELD(high), FIELD(open), FIELD(close), FIELD(adjClose), FIELD(volume)));
};

struct XQuote 
{
    time_t  date;
    double  low;
    double  high;
    double  open;
    double  close;
    double  adjClose;
    db_int8 volume;
};

/**
 * Abstract provider of stack data
 */
class StockDataProvider
{
  public:
    /**
     * Download data from provider
     * @param file placeholder for file name where results are stored
     * @param error placeholder for error message (if method returns flase)
     * @param symbol stock symbol
     * @param from first date (0 means not specified)
     * @param till last date (0 means not specified)
     * @return error code
     */
    virtual ErrorCode download(string& file, string& error, string const& symbol, date_t from = 0, date_t till = 0) = 0;

    /**
     * Release file returned by download
     */
    virtual void releaseFile(string const& file) = 0;

    /**
     * Data is provided in descending order
     */
    virtual bool descendingOrder() = 0;
};

class YahooStockDataProvider : public StockDataProvider
{
    bool cache;
  public:
    virtual ErrorCode download(string& file, string& error, string const& symbol, date_t from = 0, date_t till = 0);
    virtual void releaseFile(string const& file);
    virtual bool descendingOrder();

    YahooStockDataProvider(bool cache = true) { 
        this->cache = cache;
    }
};

class Processor 
{    
  public:
    static map<string, Processor*> processors;

    class Callback {
      protected:
        FILE* out;
      public:
        virtual void apply(Quote const& quote) = 0;
        virtual ~Callback() {}

        Callback(FILE* file) : out(file) {}
    };

    virtual Callback* getCallback(FILE* output) = 0;
    virtual ~Processor() {}

    static Processor* findProcessor(char const* name) {
        map<string, Processor*>::const_iterator i = processors.find(name);
        return i != processors.end() ? i->second : NULL;
    }
            

  protected:
    Processor(char const* name) {
        processors[name] = this;
    }
};


class StockDB 
{
  public:
    /**
     * Add quotes for the specifiedd sumbol
     * @param symbol stock symbol
     * @param quotes stock quotes
     */
    void addQuotes(char const* symbol, vector<Quote> const& quotes);
    
    /**
     * Import CSV file
     * @param symbol stock symbol
     * @param csvFileName name of file in CSV format (for example obtained from http://ichart.finance.yahoo.com/ 
     * @param nImported placeholder for number of imported quotes
     * @param skipHeader if true then skip foirst line of the file containing column names
     * @return error code
     */
    ErrorCode importQuotes(char const* symbol, char const* csvFileName, size_t& nImported, bool skipHeader = true);

     /**
     * Import CSV file
     * @param symbol stock symbol
     * @param csvData opened file with data in CVS format
     * @return error code
     */
    ErrorCode importQuotes(char const* symbol, FILE* csvData, size_t& nImported);

    /**
     * Save changes to the disk (commit transaction)
     */
    void flush();

    /**
     * Backup database 
     * @param filePath target file for backup
     * @param compact whther to perfrom compactification of database during backup ot not
     */
    bool backup(char const* filePath, bool compact);

    /**
     * Get stock quotes for the specified period
     * @param symbol stock symbol
     * @param from first date (inclusive)
     * @param till last date (inclusive)
     * @return vector of quotes
     */
    vector<Quote> getQuotes(char const* symbol, date_t from, date_t till);

    /** 
     * Extract stock quotes to file in CSV format
     * @param symbol stock symbol
     * @param from first date (inclusive)
     * @param till last date (inclusive)
     * @param csvFileName path to the destination name
     * @param writeHeader add line with olumn names atthe beginning of the file
     * @return error code
      */
    ErrorCode exportQuotes(char const* symbol, date_t from, date_t till, char const* csvFileName, bool writeHeader = true);

    /** 
     * Extract stock quotes to file in CSV format
     * @param symbol stock symbol
     * @param from first date (inclusive)
     * @param till last date (inclusive)
     * @param csvFile opened file
     * @return error code
     */
    ErrorCode exportQuotes(char const* symbol, date_t from, date_t till, FILE* csvFile);

     /** 
     * Process quotes and output result to the CSV file
     * @param proc processor
     * @param symbol stock symbol
     * @param from first date (inclusive)
     * @param till last date (inclusive)
     * @param csvFile opened file
     * @return error code
     */
    ErrorCode processQuotes(Processor* proc, char const* symbol, date_t from, date_t till, FILE* csvFile);

     /** 
     * Process quotes and output result to the CSV file
     * @param procName processor name
     * @param symbol stock symbol
     * @param from first date (inclusive)
     * @param till last date (inclusive)
     * @param csvFile opened file
     * @return error code
     */
    ErrorCode processQuotes(char const* procName, char const* symbol, date_t from, date_t till, FILE* csvFile);

    /**
     * Remove specified range of quotes
     * @param symbol stock symbol
     * @param from first date (inclusive)
     * @param till last date (inclusive)
     * @return number of removed quotes
     */
    int removeQuotes(char const* symbol, date_t from, date_t till);

    /**
     * Get list of stock symbols available in the database
     * @return vector of stock symbols
     */
    vector<string> getSymbols();

    /**
     * Get information about stock 
     * @param symbol stock symbol
     * @return stock symbol, range and number of quotes
     */
    Stock getStock(char const* symbol);

    /**
     * Get information about all stocks available in the database
     * @return vector of stocks
     */
    vector<Stock> getStocks();

    /**
     * Database constructor
     * @parovider provider for downloading data
     */
    StockDB(StockDataProvider* provider = NULL);

    /**
     * Open database
     * @param databaseFilePath path to database file
     * @return error code
     */     
    ErrorCode open(char const* databaseFilePath);

    /**
     * Close database
     */
    void close();

    /**
     * Load data from ichart.finance.yahoo.com
     * @param symbolsFileName file with list of symbols to be uploaded
     * @param from first date (inclusive)
     * @param till last date (inclusive)
     * @param progress output to the stdout information about uploaded symbols
     * @return error code
     */          
    ErrorCode load(char const* symbolsFileName, date_t from = 0, date_t till = 0, bool progress = true);
    
    /**
     * Load data from ichart.finance.yahoo.com
     * @param symbol staoc symbol
     * @param from first date (inclusive)
     * @param till last date (inclusive)
     * @param progress output to the stdout information about uploaded symbols
     * @return error code
     */          
    ErrorCode loadSymbol(char const* symbol, date_t from = 0, date_t till = 0, bool progress = true);
    
    /**
     * Update data from server
     */
    ErrorCode update(bool progress = true);

    /**
     * Get error text. Error text is set when returned ErorCode is not OK
     */
    string const& getErrorMessage() const 
    { 
        return error;
    }

    /**
     * Set data provider
     */
    void registerDataProvider(StockDataProvider* newProvider) { 
        provider = newProvider;
    }
    

    static char* printDate(date_t date, char* buf, size_t bufSize);
    static bool  parseDate(date_t& date, char const* buf);
    static string format(char const* fmt, ...);

  private:
    class QuoteProcessor : public dbTimeSeriesProcessor<Quote>
    {
      public:
        QuoteProcessor(dbDatabase& database);
        virtual void process(Quote const& quote, void* ctx = NULL);
    };

    string error;
    dbDatabase db;
    QuoteProcessor processor;
    StockDataProvider* provider;
};

#endif    
